﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using UserControlLib.Models;

namespace UserControlLib
{
    /// <summary>
    /// SpeedGauge.xaml 的交互逻辑
    /// </summary>

    public partial class SpeedGauge : UserControl
    {
        /// <summary>
        /// 最小值
        /// </summary>
        public static readonly DependencyProperty MinProperty =
      DependencyProperty.Register("Min", typeof(int), typeof(SpeedGauge), new PropertyMetadata(OnMinPropertyChanged));
        public int Min
        {
            get { return (int)GetValue(MinProperty); }
            set { SetValue(MinProperty, value); }
        }
        private static void OnMinPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SpeedGauge speedGauge = d as SpeedGauge;
            speedGauge.model.Min=(int)e.NewValue;
            speedGauge.RenderGauge();
        }

        /// <summary>
        /// 最大值
        /// </summary>
        public static readonly DependencyProperty MaxProperty =
      DependencyProperty.Register("Max", typeof(int), typeof(SpeedGauge), new PropertyMetadata(OnMaxPropertyChanged));
        public int Max
        {
            get { return (int)GetValue(MaxProperty); }
            set { SetValue(MaxProperty, value); }
        }
        private static void OnMaxPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SpeedGauge speedGauge = d as SpeedGauge;
            speedGauge.model.Max = (int)e.NewValue;
            speedGauge.RenderGauge();
        }

        /// <summary>
        /// 指针值
        /// </summary>
        public static readonly DependencyProperty ValueProperty =
      DependencyProperty.Register("Value", typeof(int), typeof(SpeedGauge), new PropertyMetadata(OnValuePropertyChanged));
        public int Value
        {
            get { return (int)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SpeedGauge speedGauge = d as SpeedGauge;
            speedGauge.model.Value = (int)e.NewValue;
            speedGauge.RenderGauge();
        }



        /// <summary>
        /// 标题
        /// </summary>
        public static readonly DependencyProperty TitleProperty =
      DependencyProperty.Register("Title", typeof(string), typeof(SpeedGauge), new PropertyMetadata(OnTitlePropertyChanged));
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }
        private static void OnTitlePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SpeedGauge speedGauge = d as SpeedGauge;
            speedGauge.model.Title = (string)e.NewValue;
        }

        /// <summary>
        /// 单位
        /// </summary>
        public static readonly DependencyProperty UnitProperty =
      DependencyProperty.Register("Unit", typeof(string), typeof(SpeedGauge), new PropertyMetadata(OnUnitPropertyChanged));
        public string Unit
        {
            get { return (string)GetValue(UnitProperty); }
            set { SetValue(UnitProperty, value); }
        }
        private static void OnUnitPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SpeedGauge speedGauge = d as SpeedGauge;
            speedGauge.model.Unit = (string)e.NewValue;
        }

        /// <summary>
        /// 显示值
        /// </summary>
        public static readonly DependencyProperty ValueStringProperty =
      DependencyProperty.Register("ValueString", typeof(int), typeof(SpeedGauge),new FrameworkPropertyMetadata(0,new PropertyChangedCallback(OnValueStringPropertyChanged)));
        public int ValueString
        {
            get { return (int)GetValue(ValueStringProperty); }
            set { SetValue(ValueStringProperty, value); }
        }
        private static void OnValueStringPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SpeedGauge speedGauge = d as SpeedGauge;
            speedGauge.model.ValueString = (int)e.NewValue;
        }

        /// <summary>
        /// 刻度比例
        /// </summary>
        public static readonly DependencyProperty TickUnitProperty =
      DependencyProperty.Register("TickUnit", typeof(string), typeof(SpeedGauge), new PropertyMetadata(OnTickUnitPropertyChanged));
        public string TickUnit
        {
            get { return (string)GetValue(TickUnitProperty); }
            set { SetValue(TickUnitProperty, value); }
        }
        private static void OnTickUnitPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SpeedGauge speedGauge = d as SpeedGauge;
            speedGauge.model.TickUnit = (string)e.NewValue;
        }

        /// <summary>
        /// 表盘刻度标签数量
        /// </summary>
        public static readonly DependencyProperty TickGapProperty =
      DependencyProperty.Register("TickGap", typeof(int), typeof(SpeedGauge), new PropertyMetadata(OnTickGapPropertyChanged));
        public int TickGap
        {
            get { return (int)GetValue(TickGapProperty); }
            set { SetValue(TickGapProperty, value); }
        }
        private static void OnTickGapPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SpeedGauge speedGauge = d as SpeedGauge;
            speedGauge.RenderTicks();
        }


        private SpeedGaugeModel model;

        private const double TOTAL_ANGLE = 270;//刻度面板总的角度
        private const double OFFSET_ANGLE = 90;//弧度计算偏移角度
        private const double POINTOFFSET_ANGLE = 225;//指针偏移角度（180+45）
        private const double LABLEOFFSET_ANGLE = 135;//标签偏移角度（135）

        public delegate int RealValueDeleget(int value);
        public RealValueDeleget GetRealValue { get; set; }

        private Style tickStyle;

        /// <summary>
        /// 实现渲染表盘指针的方法
        /// </summary>
        private void RenderGauge()
        {
            if (GetRealValue == null) return;

            //1、值有效性校验
            int value = Value >= Max ? Max : Value <= Min ? Min : Value;
            int offset = 190;
            double angle = TOTAL_ANGLE / (Max - Min) * (Value - Min);//计算角度
            //计算弧度 加上90度偏移值(面板有90度是无效的)
            double rad = (Math.PI / 180) * (angle + OFFSET_ANGLE);

            //计算弧线终点坐标
            double x = offset * Math.Cos(rad) + offset;
            double y = offset * Math.Sin(rad) + offset;
            Point point = new Point(x, y);
            arcSegment.IsLargeArc = angle > 180;
            arcSegment.Point = point;
 
            // 创建并配置动画
            DoubleAnimation rotateAnimation = new DoubleAnimation
            {
                To = angle + POINTOFFSET_ANGLE,
                Duration = TimeSpan.FromMilliseconds(500),
                EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut, Exponent = 5 }
            };

            Int32Animation valueAnimation = new Int32Animation
            {
                To = GetRealValue(value),
                Duration = TimeSpan.FromMilliseconds(500),
                EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut, Exponent = 5 }
            };

            // 设置动画目标和属性
            Storyboard.SetTargetName(rotateAnimation, "point");
            Storyboard.SetTargetProperty(rotateAnimation, new PropertyPath(RotateTransform.AngleProperty));

            Storyboard.SetTarget(valueAnimation, this);
            Storyboard.SetTargetProperty(valueAnimation, new PropertyPath(SpeedGauge.ValueStringProperty));

            // 开始动画
            Storyboard sb = new Storyboard();
            sb.Children.Add(rotateAnimation);
            sb.Children.Add(valueAnimation);
            sb.Begin(this);

        }

        /// <summary>
        /// 绘制表盘刻度值
        /// </summary>
        private void RenderTicks()
        {
            if ((Max - Min) < TickGap) return;

            tick_container.Children.Clear();

            int ticks = (int)(Max - Min) / TickGap + 1;

            TextBlock[] textBlocks = new TextBlock[ticks];
            for (int i = 0; i < ticks; i++)
            {
                textBlocks[i] = new TextBlock();
                var val = Min + (Max - Min) / (ticks - 1) * i;
                textBlocks[i].Text = string.Format("{0:F0}", val);
                SetTickDefaultStyle(textBlocks[i]);
            }

            double tick_angle_offset = TOTAL_ANGLE / (textBlocks.Length - 1);
            int tick_start_offset = 100;
            int tick_dest_offset = 160;

            ExponentialEase exponentialEase = new ExponentialEase();
            exponentialEase.EasingMode = EasingMode.EaseOut;
            exponentialEase.Exponent = 1;
            Duration duration = new Duration(TimeSpan.FromMilliseconds(500));

            List<string> names= new List<string>();
            int delay = 100;
            Storyboard storyboard = new Storyboard();

            for (int i = 0; i < textBlocks.Length; i++)
            {
                double angle=i*tick_angle_offset+LABLEOFFSET_ANGLE;
                double rad = (Math.PI / 180) * angle;
                double x= tick_dest_offset * Math.Cos(rad);
                double y= tick_dest_offset * Math.Sin(rad);

                double start_x = tick_start_offset * Math.Cos(rad);
                double start_y = tick_start_offset * Math.Sin(rad);

                //设置文本位置
                TranslateTransform translateTransform = new TranslateTransform(start_x, start_y);
                textBlocks[i].RenderTransform = translateTransform;

                string translateTransformName = "translate" + translateTransform.GetHashCode();
                RegisterName(translateTransformName, translateTransform);
                names.Add(translateTransformName);
                tick_container.Children.Add(textBlocks[i]);


                DoubleAnimation translate_x= new DoubleAnimation();
                translate_x.To = x;
                translate_x.BeginTime = TimeSpan.FromMilliseconds(i*delay);
                translate_x.Duration = duration;
                translate_x.EasingFunction = exponentialEase;
                Storyboard.SetTargetName(translate_x,translateTransformName);
                Storyboard.SetTargetProperty(translate_x,new PropertyPath(TranslateTransform.XProperty));

                DoubleAnimation translate_y = new DoubleAnimation();
                translate_y.To = y;
                translate_y.BeginTime = TimeSpan.FromMilliseconds(i * delay);
                translate_y.Duration = duration;
                translate_y.EasingFunction = exponentialEase;
                Storyboard.SetTargetName(translate_y, translateTransformName);
                Storyboard.SetTargetProperty(translate_y, new PropertyPath(TranslateTransform.YProperty));

                storyboard.Children.Add(translate_x);
                storyboard.Children.Add(translate_y);
            }

            storyboard.Begin(this);
            storyboard.Completed += (sender, e) =>
            {
                foreach (string name in names)
                {
                    UnregisterName(name);
                }
            };
        }
        /// <summary>
        /// 样式
        /// </summary>
        /// <param name="textBlock"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void SetTickDefaultStyle(TextBlock textBlock)
        {
            if (textBlock == null) return;
            textBlock.Style = tickStyle;
        }

        public SpeedGauge()
        {
            InitializeComponent();

            tickStyle = this.Resources["TickStyle"] as Style;

            model = this.Resources["model"] as SpeedGaugeModel;
        }
    }
}
